package sf.database.jpa;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import sf.core.DBField;
import sf.core.DBObject;
import sf.database.dao.DBClient;
import sf.database.meta.MetaHolder;
import sf.database.meta.TableMapping;
import sf.database.util.OrmUtils;
import sf.dsl.example.Criteria;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

public class JpaEntityManagerImpl implements JpaEntityManager {
    private static final Logger logger = LoggerFactory.getLogger(JpaEntityManagerImpl.class);
    private FlushModeType mode = FlushModeType.AUTO;
    private Map<String, Object> properties;
    private JpaEntityManagerFactory jemf;
    private DBClient client;
    private Connection connection;

    public JpaEntityManagerImpl(Connection connection) {

    }

    public JpaEntityManagerImpl(EntityManagerFactory emf, Map<String, Object> properties) {
        super();
        this.jemf = (JpaEntityManagerFactory) emf;
        this.properties = properties;
        this.client = jemf.getDb();
    }

    @Override
    public void persist(Object entity) {
        client.insert((DBObject) entity);
    }

    @Override
    public <T> T merge(T entity) {
        client.merge((DBObject) entity);
        return entity;
    }

    @Override
    public void remove(Object entity) {
        client.delete((DBObject) entity);
    }

    @Override
    public <T> T find(Class<T> entityClass, Object primaryKey) {
        if (primaryKey == null) {
            return null;
        }
        try {
            DBObject data = (DBObject) entityClass.getDeclaredConstructor().newInstance();
            OrmUtils.setPrimaryKeyValue(data, primaryKey);
            return (T) client.selectOne(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
        try {
            DBObject data = (DBObject) entityClass.getDeclaredConstructor().newInstance();
            TableMapping tableMapping = MetaHolder.getMeta(entityClass);
            if (primaryKey != null) {
                OrmUtils.setPrimaryKeyValue(data, primaryKey);
            } else if (properties != null && properties.size() > 0) {
                Criteria c = data.useQuery().createCriteria();
                boolean flag = false;
                for (String s : properties.keySet()) {
                    DBField f = tableMapping.getFields().get(s);
                    if (f == null) {
                        logger.error("No Field named " + s + " in bean " + data.getClass().getName());
                        continue;
                    }
                    if (flag) {
                        c.and();
                    }
                    c.eq(f, properties.get(s));
                    flag = true;
                }
            } else {
                data.useQuery().clear();
            }
            return (T) client.selectOne(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
        return find(entityClass, primaryKey);
    }

    @Override
    public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
        return find(entityClass, primaryKey, properties);
    }

    @Override
    public <T> T getReference(Class<T> entityClass, Object primaryKey) {
        return find(entityClass, primaryKey);
    }

    @Override
    public void flush() {

    }

    @Override
    public void setFlushMode(FlushModeType flushMode) {
        this.mode = flushMode;
    }

    @Override
    public FlushModeType getFlushMode() {
        return mode;
    }

    @Override
    public void lock(Object entity, LockModeType lockMode) {

    }

    @Override
    public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {

    }

    @Override
    public void refresh(Object entity) {
        if (entity instanceof DBObject) {
            DBObject newObj = client.selectOne((DBObject) entity);
            newObj.stopUpdate();
            BeanUtils.copyProperties(newObj, entity);
        }
    }

    @Override
    public void refresh(Object entity, Map<String, Object> properties) {
        if (entity instanceof DBObject) {
            DBObject data = (DBObject) entity;
            TableMapping tableMapping = MetaHolder.getMeta(entity.getClass());
            if (properties != null && properties.size() > 0) {
                Criteria c = data.useQuery().createCriteria();
                boolean flag = false;
                for (String s : properties.keySet()) {
                    DBField f = tableMapping.getFields().get(s);
                    if (f == null) {
                        logger.error("No Field named " + s + " in bean " + data.getClass().getName());
                        continue;
                    }
                    if (flag) {
                        c.and();
                    }
                    c.eq(f, properties.get(s));
                    flag = true;
                }
            }
            DBObject newObj = client.selectOne(data);
            newObj.stopUpdate();
            BeanUtils.copyProperties(newObj, entity);
        }
    }

    @Override
    public void refresh(Object entity, LockModeType lockMode) {
        refresh(entity);
    }

    @Override
    public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
        refresh(entity, properties);
    }

    @Override
    public void clear() {

    }

    @Override
    public void detach(Object entity) {
        //清理一级缓存
    }

    @Override
    public boolean contains(Object entity) {
        if (entity instanceof DBObject) {
            DBObject newObj = client.selectOne((DBObject) entity);
            return newObj != null;
        }
        return false;
    }

    @Override
    public LockModeType getLockMode(Object entity) {
        return LockModeType.NONE;
    }

    @Override
    public void setProperty(String propertyName, Object value) {
        properties.put(propertyName, value);
    }

    @Override
    public Map<String, Object> getProperties() {
        return properties;
    }

    @Override
    public Query createQuery(String qlString) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public Query createQuery(CriteriaUpdate updateQuery) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public Query createQuery(CriteriaDelete deleteQuery) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public Query createNamedQuery(String name) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public Query createNativeQuery(String sqlString) {
        return null;
    }

    @Override
    public Query createNativeQuery(String sqlString, Class resultClass) {
        return null;
    }

    @Override
    public Query createNativeQuery(String sqlString, String resultSetMapping) {
        return null;
    }

    @Override
    public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
        throw new UnsupportedOperationException("not support!");
    }

    @Override
    public void joinTransaction() {

    }

    @Override
    public boolean isJoinedToTransaction() {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> cls) {
        try {
            return cls.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new PersistenceException(e);
        }
    }

    @Override
    public Object getDelegate() {
        return null;
    }

    @Override
    public void close() {

    }

    @Override
    public boolean isOpen() {
        return false;
    }

    @Override
    public EntityTransaction getTransaction() {
        return null;
    }

    @Override
    public EntityManagerFactory getEntityManagerFactory() {
        return jemf;
    }

    @Override
    public CriteriaBuilder getCriteriaBuilder() {
        throw new UnsupportedOperationException("不支持的操作!");
    }

    @Override
    public Metamodel getMetamodel() {
        return null;
    }

    @Override
    public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
        throw new UnsupportedOperationException("不支持的操作!");
    }

    @Override
    public EntityGraph<?> createEntityGraph(String graphName) {
        throw new UnsupportedOperationException("不支持的操作!");
    }

    @Override
    public EntityGraph<?> getEntityGraph(String graphName) {
        throw new UnsupportedOperationException("不支持的操作!");
    }

    @Override
    public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
        throw new UnsupportedOperationException("不支持的操作!");
    }

    @Override
    public DBClient getClient() {
        return client;
    }
}
